PollutionVol = function (rtTexture) {
  var meshSecondPass;
  var materialSecondPass;
  var cubeTextures = ["PM10", "PM2.5", "SO2", "NO2", "CO", "NO"];

  var fragmentShaderFirstPass = /* glsl  */ `
    		varying vec3 localPos; 
			void main()	
	        {	
	            gl_FragColor = vec4(localPos.xyz,1); 
            }`;

  var vertexShaderFirstPass = /* glsl  */ `
    		varying vec3 localPos; 
			void main() 
			{ 
				localPos = position + vec3(0.5, 0.5, 0.5); 
				gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 ); 
            }`;

  var fragmentShaderSecondPass = /* glsl  */ `
    		varying vec3 worldSpaceCoords; 
			varying vec4 projectedCoords; 
			uniform sampler2D _tex, _cubeTex, _transferTex; 
			uniform float _Steps; 
			uniform float _Position; 
			uniform float _WindowSize; 
         	uniform int _Frame;
         	uniform int _Internum;
			uniform float _Intensity;
			uniform float _YMin;
			uniform float _YMax;
			uniform float _XMin;
			uniform float _XMax;
			uniform float _ZPos;
			const int MAX_STEPS = 256; 
			vec4 sampleAs3DTexture( vec3 texCoord) 
			{ 
				float colrow = 8.0; 
				vec4 colorSlice1, colorSlice2; 
				vec2 texCoordSlice1, texCoordSlice2; 
				float maxslice = colrow*colrow-1.0; 
				float zSliceNumber1 = floor(texCoord.z  * maxslice); 
				
				float zSliceNumber2 = min(zSliceNumber1 + 1.0, maxslice); //Clamp to maxslice 
				texCoord.xy /= colrow; 
				texCoordSlice1 = texCoordSlice2 = texCoord.xy; 
				
				texCoordSlice1.x += (mod(zSliceNumber1, colrow ) / colrow); 
				texCoordSlice1.y += floor((maxslice - zSliceNumber1) / colrow) / colrow; 
				texCoordSlice2.x += (mod(zSliceNumber2, colrow ) / colrow);  
				texCoordSlice2.y += floor((maxslice - zSliceNumber2) / colrow) / colrow;  
				
				colorSlice1 = texture2D(_cubeTex, texCoordSlice1);  
				colorSlice2 = texture2D(_cubeTex, texCoordSlice2);  
				
				float zDifference = mod(texCoord.z * maxslice, 1.0); 
				
				return mix(colorSlice1, colorSlice2, zDifference) ; 
			} 
			
			vec4 getColorOfStep(int step, vec3 currentPosition, int frame) 
			{  
				float colrow = 8.0; 
				int row = step/5;  
				int column = step - row*5; 
				vec4 col = sampleAs3DTexture( vec3((currentPosition.x+float(column))*0.2, (currentPosition.z+float(row))*0.2, float(frame)/(colrow*colrow))); 
				return vec4(col.a);
			} 
			
			void main( void ) {  
				vec2 texc = vec2(((projectedCoords.x / projectedCoords.w) + 1.0 ) / 2.0, 
								((projectedCoords.y / projectedCoords.w) + 1.0 ) / 2.0 ); 
				vec3 backPos = texture2D(_tex, texc).xyz;  
				vec3 frontPos = worldSpaceCoords; 
				vec3 dir = backPos - frontPos; 
				float rayLength = length(dir); 
				float delta = 1.0 / _Steps; 
				
				vec3 deltaDirection = normalize(dir) * delta;  
				float deltaDirectionLength = length(deltaDirection);  
				
				vec3 currentPosition = frontPos;   
				vec4 accumulatedColor = vec4(0.0); 
				float accumulatedAlpha = 0.0;  
				float accumulatedLength = 0.0;  
				
				float minpos = _Position*_Intensity;  
				float maxpos = min(minpos+_WindowSize,1.0);  
				float minmax = maxpos - minpos;  
				
				int frame = _Frame/_Internum;  
				int framedelta = _Frame - frame*_Internum; 
				float frameratio = float(framedelta) /float(_Internum); 
				
				vec4 colorSample;  
				float alphaSample;  
				
				for(int i = 0; i < MAX_STEPS; i++)   
				{
					if(currentPosition.x < _XMin || currentPosition.x > _XMax  
						||currentPosition.z < _YMin || currentPosition.z > _YMax	
						|| currentPosition.y > _ZPos) 
					{ 
						currentPosition += deltaDirection; 
						accumulatedLength += deltaDirectionLength; 
						if(accumulatedLength >= rayLength || accumulatedAlpha > 0.99) 
						{ 
							break; 
						} 
						continue; 
					} 
					
					int step = int(currentPosition.y * 24.0);   
					vec4 colorSample0 = getColorOfStep(step, currentPosition, frame); 
					vec4 colorSample1 = getColorOfStep(step+1, currentPosition, frame); 
					float interratio = currentPosition.y * 24.0 - float(step); 
					vec4 curcolorSample = colorSample0 + (colorSample1 - colorSample0) * interratio; 
					
					vec4 nextframecolorSample0 = getColorOfStep(step, currentPosition, frame+1);
					vec4 nextframecolorSample1 = getColorOfStep(step+1, currentPosition, frame+1); 
					vec4 nextcolorSample = nextframecolorSample0 + (nextframecolorSample1 - nextframecolorSample0) * interratio; 
					
					colorSample = curcolorSample+(nextcolorSample-curcolorSample)*frameratio; 
					colorSample = clamp((colorSample - minpos)/minmax, vec4(0), vec4(1)); 
					
					alphaSample = colorSample.a * colorSample.a; 
					accumulatedColor += (1.0 - accumulatedAlpha) * colorSample * alphaSample; 
					accumulatedAlpha = alphaSample + (1.0 - alphaSample) * accumulatedAlpha; 
					
					currentPosition += deltaDirection; 
					accumulatedLength += deltaDirectionLength; 
					
					if(accumulatedLength >= rayLength || accumulatedAlpha > 0.99)  
					{
						break; 
					}
				}
				
				gl_FragColor = accumulatedColor; 
				gl_FragColor.rgb = vec3(1) - gl_FragColor.rgb;
				float ratio = (1.0-gl_FragColor.r);
				gl_FragColor.rgb = texture2D(_transferTex, vec2(ratio*_Intensity,0.5)).xyz;
				gl_FragColor.a = accumulatedAlpha;
            }`;

  var vertexShaderSecondPass = /* glsl  */ `
    		varying vec3 worldSpaceCoords;  
			varying vec4 projectedCoords;   
			void main()  
         	{ 
		            worldSpaceCoords = position + vec3(0.5, 0.5,0.5);  
					gl_Position = projectionMatrix *  modelViewMatrix * vec4( position, 1.0 );  
					projectedCoords =  projectionMatrix * modelViewMatrix * vec4( position, 1.0 );  
            }`;

  this.createFirstPassMesh = function () {
    var materialFirstPass = new THREE.ShaderMaterial({
      vertexShader: vertexShaderFirstPass,
      fragmentShader: fragmentShaderFirstPass,
      side: THREE.BackSide,
    });

    var boxGeometry = new THREE.BoxGeometry(1.0, 1.0, 1.0);

    var meshFirstPass = new THREE.Mesh(boxGeometry, materialFirstPass);
    meshFirstPass.scale.y = 0.6;
    meshFirstPass.scale.x = 1.53;
    meshFirstPass.scale.z = 1.0;

    return meshFirstPass;
  };

  this.createSecondPassMesh = function () {
    this.loadTextures("PM10");
    this.loadTextures("NO");
    this.loadTextures("CO");
    this.loadTextures("NO2");
    this.loadTextures("SO2");
    this.loadTextures("PM2_5");

    materialSecondPass = new THREE.ShaderMaterial({
      vertexShader: vertexShaderSecondPass,
      fragmentShader: fragmentShaderSecondPass,
      side: THREE.FrontSide,
      uniforms: {
        _tex: { type: "t", value: rtTexture },
        _transferTex: { type: "t", value: transferTexture },
        _cubeTex: { type: "t", value: cubeTextures["PM10"] },
        _Steps: { type: "1f", value: 126 },
        _Position: { type: "1f", value: 0.0 },
        _WindowSize: { type: "1f", value: 1.0 },
        _Frame: { type: "1i", value: 0 },
        _Internum: { type: "1i", value: 10 },
        _Intensity: { type: "1f", value: 1 },
        _YMin: { type: "1f", value: 0.0 },
        _YMax: { type: "1f", value: 1.0 },
        _XMin: { type: "1f", value: 0.0 },
        _XMax: { type: "1f", value: 1.0 },
        _ZPos: { type: "1f", value: 1.0 },
      },
    });
    materialSecondPass.transparent = true;
    materialSecondPass.blending = THREE["NormalBlending"];

    var boxGeometry = new THREE.BoxGeometry(1.0, 1.0, 1.0);

    meshSecondPass = new THREE.Mesh(boxGeometry, materialSecondPass);
    meshSecondPass.scale.y = 0.6;
    meshSecondPass.scale.x = 1.53;
    meshSecondPass.scale.z = 1.0;
    return meshSecondPass;
  };

  this.loadTextures = function (name) {
    var loader = new THREE.TextureLoader();
    cubeTextures[name] = loader.load(name + ".png");
    cubeTextures[name].generateMipmaps = false;
    cubeTextures[name].minFilter = THREE.LinearFilter;
    cubeTextures[name].magFilter = THREE.LinearFilter;
  };

  this.setVisible = function (value) {
    meshSecondPass.visible = value;
  };

  this.changeVol = function (name) {
    materialSecondPass.uniforms._cubeTex.value = cubeTextures[name];
  };

  this.render = function () {
    materialSecondPass.uniforms._Frame.value =
      (materialSecondPass.uniforms._Frame.value + 1) % 240;
  };

  this.setXMin = function (value) {
    materialSecondPass.uniforms._XMin.value = guiControls.xmin;
  };

  this.setXMax = function (value) {
    materialSecondPass.uniforms._XMax.value = guiControls.xmax;
  };

  this.setYMin = function (value) {
    materialSecondPass.uniforms._YMin.value = guiControls.ymin;
  };

  this.setYMax = function (value) {
    materialSecondPass.uniforms._YMax.value = guiControls.ymax;
  };

  this.setZPos = function (value) {
    materialSecondPass.uniforms._ZPos.value = guiControls.zpos;
  };

  this.setCull = function (value) {
    materialSecondPass.uniforms._Position.value = guiControls.cull;
  };

  this.setIntensity = function (value) {
    materialSecondPass.uniforms._Intensity.value = guiControls.intensity;
  };

  this.isVisible = function () {
    if (materialSecondPass == null) return false;

    return materialSecondPass.visible;
  };
};
